package com.tcm.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tcm.common.enums.CachePrefix;
import com.tcm.config.AppProperties;
import com.tcm.entity.*;
import com.tcm.entity.dto.ImageUploadDto;
import com.tcm.entity.dto.param.StuUserInfoUpdateParam;
import com.tcm.entity.dto.param.UserAdminInsertParam;
import com.tcm.entity.dto.param.UserAdminUpdateParam;
import com.tcm.entity.dto.param.UserInfoUpdateParam;
import com.tcm.entity.resp.PageR;
import com.tcm.entity.vo.*;
import com.tcm.exception.DatabaseOperationException;
import com.tcm.exception.EntityInvalidException;
import com.tcm.exception.UserTypeNotInvalidException;
import com.tcm.mapper.RoleUserMapper;
import com.tcm.mapper.UserMapper;
import com.tcm.service.*;
import com.tcm.utils.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;
import org.springframework.web.multipart.MultipartFile;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 用户业务实现类
 * @author Guqier
 * @version 1.0
 * @date 2023/7/1 22:36
 */
@Slf4j
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    @Autowired
    private AppProperties appProperties;

    @Autowired
    private RoleUserMapper roleUserMapper;

    @Autowired
    private RoleService roleService;

    @Autowired
    private ImageService imageService;

    @Autowired
    private StudentService studentService;

    @Autowired
    private SocialStudentService socialStudentService;

    @Autowired
    private WxUserService wxUserService;

    @Autowired
    private PermissionService permissionService;

    private static final int STUDENT_TYPE = 2;

    private static final int TEACHER_TYPE = 1;

    private static final int ADMIN_TYPE = 0;

    private static final String ADMIN_ROLE_KEY = "admin";

    @Autowired
    private RedisUtil redisUtil;

    @Override
    public UserInfoResult getUserInfo(Long currUserId){
        UserInfoResult userInfoResult = new UserInfoResult();

        // 该分支为获取当前用户信息
        User one = this.getById(currUserId);

        // 将用户信息封装到数据视图对象
        BeanUtils.copyProperties(one, userInfoResult);

        if (userInfoResult.getAvatar() != null) {
            userInfoResult.setAvatarUrl(imageService.getImageById(userInfoResult.getAvatar()).getRemoteFileUrl());
        }

        // 查询权限
        List<String> userRoleKey = roleService.getUserRoleKey(userInfoResult.getId());
        List<String> permissionKey = permissionService.getPermissionKeyByUserId(currUserId);
        userInfoResult.setRole(userRoleKey);
        userInfoResult.setPermissionKey(permissionKey);
        return userInfoResult;
    }

    /**
     * 通过账号获取用户信息
     * @param account 账号
     * @return 用户信息
     */
    @Override
    public User getUserByAccount(String account){
        User user = redisUtil.getCacheObject(CachePrefix.PC_USER_INFO.getCacheKey(account));
        if (user == null) {
            LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(User::getAccount, account);
            user = this.getOne(queryWrapper);
            redisUtil.setCacheObject(CachePrefix.PC_USER_INFO.getCacheKey(account), user, 1, TimeUnit.HOURS);
        }
        return user;
    }

    /**
     * 获取新用户对象
     * @param account 用户账号
     * @param name 姓名
     * @param nickname 昵称，默认与姓名一致
     * @param type 用户类型，0表示学生，1表示老师，,2表示管理员
     * @param gender 性别，0表示女生，1表示男生
     * @param email 电子邮箱
     * @param phone 手机号码
     * @param currUserId 当前登录用户ID
     * @return 新用户对象
     */
    @Override
    public User getNewFillInfoUser(String account, String name, String nickname, int type, int gender, String email, String phone, Long currUserId){
        String salt = UUID.randomUUID().toString();
        User user = new User();
        user.setAccount(account);
        user.setPassword(DigestUtils.md5DigestAsHex((salt + (type == STUDENT_TYPE ? appProperties.getDefaultPasswordStudent() : appProperties.getDefaultPasswordTeacher())).getBytes()));
        user.setSalt(salt);
        user.setName(name);
        user.setNickname(nickname != null ? nickname : name);
        user.setUserType(type);
        user.setGender(gender);
        user.setEmail(email);
        user.setPhone(phone);
        user.setCreateUser(currUserId);
        user.setUpdateUser(currUserId);

        return user;
    }

    /**
     * 新增管理员用户
     * @param adminUpdateParam 管理员用户参数
     * @param currUserId 当前登录用户ID
     * @return true为成功
     * @throws DatabaseOperationException 数据库操作失败异常
     */
    @Override
    @Transactional
    public boolean addAdminUser(UserAdminInsertParam adminUpdateParam, Long currUserId) throws DatabaseOperationException{
        // 校验用户账号是否存在
        LambdaQueryWrapper<User> userLambdaQueryWrapper = new LambdaQueryWrapper<>();
        userLambdaQueryWrapper.eq(User::getAccount, adminUpdateParam.getAccount());
        User one1 = this.getOne(userLambdaQueryWrapper);
        if (one1 != null) {
            return false;
        }
        // 给实体类赋值
        User user = new User();
        BeanUtils.copyProperties(adminUpdateParam, user);
        String salt = UUID.randomUUID().toString();
        String pwd = DigestUtils.md5DigestAsHex((salt + user.getPassword()).getBytes());
        user.setSalt(salt);
        user.setPassword(pwd);
        user.setUserType(ADMIN_TYPE);
        user.setCreateUser(currUserId);
        user.setUpdateUser(currUserId);
        user.setNickname(adminUpdateParam.getNickname() == null ? adminUpdateParam.getName() : adminUpdateParam.getNickname());
        // 插入用户
        boolean u = this.save(user);
        if (!u) {
            throw new DatabaseOperationException("数据库插入用户失败");
        }
        // 插入角色关系
        LambdaQueryWrapper<Role> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Role::getRoleKey, ADMIN_ROLE_KEY);
        Role one = roleService.getOne(queryWrapper);
        RoleUser roleUser = new RoleUser();
        roleUser.setUserId(user.getId());
        roleUser.setRoleId(one.getId());
        roleUser.setCreateUser(currUserId);
        roleUser.setUpdateUser(currUserId);
        int insert = roleUserMapper.insert(roleUser);
        if (insert != 1) {
            throw new DatabaseOperationException("数据库插入角色失败");
        }
        return true;
    }

    /**
     * 分页获取管理员用户信息
     * @param pageNum 当前页号
     * @param pageSize 页面大小
     * @param account 查询账号
     * @param name 查询名称
     * @return 分页结果
     */
    @Override
    public PageR<UserListResult> getAdminUserListByPage(Integer pageNum, Integer pageSize, String account, String name){
        Page<User> page = new Page<>(pageNum, pageSize);
        page.addOrder(OrderItem.desc("update_time"));
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getUserType, ADMIN_TYPE);
        queryWrapper.like(account != null, User::getAccount, account);
        queryWrapper.like(name != null, User::getName, name);
        queryWrapper.orderByDesc(User::getUpdateTime);
        this.page(page, queryWrapper);
        // 封装结果集
        PageR<UserListResult> result = new PageR<>();
        BeanUtils.copyProperties(page, result, "records");
        List<User> records = page.getRecords();
        List<UserListResult> users = new ArrayList<>();
        for (User record : records) {
            UserListResult userListResult = new UserListResult();
            BeanUtils.copyProperties(record, userListResult);
            users.add(userListResult);
        }
        result.setRecords(users);
        return result;
    }

    /**
     * 修改管理员用户信息
     * @param param 参数封装
     * @param currUserId 当前登录用户ID
     * @return true为成功
     */
    @Override
    public boolean modifyAdminUserInfo(UserAdminUpdateParam param, Long currUserId){
        User one = this.getById(param.getId());
        BeanUtils.copyProperties(param, one, "password");
        if (param.getPassword() != null){
            String salt = UUID.randomUUID().toString();
            String pwd = DigestUtils.md5DigestAsHex((salt + param.getPassword()).getBytes());
            one.setPassword(pwd);
            one.setSalt(salt);
        }
        one.setUpdateUser(currUserId);
        redisUtil.deleteObject(CachePrefix.PC_USER_INFO.getCacheKey(one.getAccount()));
        return this.updateById(one);
    }

    /**
     * 删除管理员用户
     * @param ids id列表
     * @param currUserId 当前登录用户ID
     * @return 删除结果，true为成功
     */
    @Override
    @Transactional
    public boolean removeAdminUser(List<String> ids, Long currUserId){
        int cnt = 0;
        for (String id : ids) {
            User one = this.getById(id);
            if (one == null){
                break;
            }
            // 删除角色关系
            LambdaQueryWrapper<RoleUser> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(RoleUser::getUserId, id);
            List<RoleUser> roleUsers = roleUserMapper.selectList(queryWrapper);
            int count = 0;
            for (RoleUser roleUser : roleUsers) {
                roleUser.setUpdateUser(currUserId);
                roleUserMapper.updateById(roleUser);
                count += roleUserMapper.deleteById(roleUser.getId());
            }
            // 删除用户
            one.setUpdateUser(currUserId);
            this.updateById(one);
            if (this.removeById(id) && count == roleUsers.size()) {
                cnt++;
            }
            redisUtil.deleteObject(CachePrefix.PC_USER_INFO.getCacheKey(one.getAccount()));
        }
        // 校验结果
        if (cnt != ids.size()){
            throw new DatabaseOperationException("数据库操作失败");
        }
        return true;
    }

    /**
     * 分页获取用户列表
     * @param pageNum 当前页码
     * @param pageSize 页面大小
     * @param account 查询账号，可选
     * @param name 查询姓名，可选
     * @param type 用户类型，可选
     * @return 分页用户列表
     */
    @Override
    public PageR<UserListResult> getUserListByPage(Integer pageNum, Integer pageSize, String account, String name, Integer type){
        // 分页查询符合条件的用户
        Page<User> page = new Page<>(pageNum, pageSize);
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.like(account != null, User::getAccount, account)
                .like(name != null, User::getName, name)
                .eq(type != null, User::getUserType, type);
        this.page(page, queryWrapper);
        // 构建结果集
        PageR<UserListResult> pageR = new PageR<>();
        BeanUtils.copyProperties(page, pageR, "records");
        // 遍历records把数据转换为结果封装类
        List<UserListResult> results = page.getRecords().stream().map(u -> {
            UserListResult userListResult = new UserListResult();
            BeanUtils.copyProperties(u, userListResult);
            // 设置响应角色类型名
            userListResult.setUserTypeName(u.getUserType().equals(ADMIN_TYPE) ? "管理员" : u.getUserType().equals(TEACHER_TYPE) ? "教师" : "学生");
            return userListResult;
        }).collect(Collectors.toList());
        pageR.setRecords(results);
        return pageR;
    }

    /**
     * 重置用户密码
     * @param id 用户ID
     * @param currUserId 当前登录用户ID
     * @return 处理结果，true为成功
     */
    @Override
    public String resetUserPassword(Long id, Long currUserId){
        User user = this.getById(id);
        if (user == null){
            return null;
        }
        // 恢复初始密码
        String salt = UUID.randomUUID().toString();
        String defaultPassword = user.getUserType() == STUDENT_TYPE ? appProperties.getDefaultPasswordStudent() : appProperties.getDefaultPasswordTeacher();
        String pwd = DigestUtils.md5DigestAsHex((salt + defaultPassword).getBytes());
        user.setSalt(salt);
        user.setPassword(pwd);
        user.setUpdateUser(currUserId);
        boolean res = this.updateById(user);
        redisUtil.deleteObject(CachePrefix.PC_USER_INFO.getCacheKey(user.getAccount()));
        return res ? user.getUserType() == STUDENT_TYPE ? appProperties.getDefaultPasswordStudent() : appProperties.getDefaultPasswordTeacher() : null;
    }

    /**
     * 修改用户自己的个人信息
     * @param param 参数封装
     * @param currUserId 当前用户ID
     * @return 修改结果，true为成功
     */
    @Override
    @Transactional
    public boolean modifyUserInfoSelf(UserInfoUpdateParam param, Long currUserId) throws Exception {
        User user = this.getById(currUserId);
        if (user == null) {
            throw new Exception("用户信息不存在");
        }
        user.setName(param.getName());
        user.setNickname(param.getName());
        user.setGender(param.getGender());
        user.setEmail(param.getEmail());
        user.setPhone(param.getPhone());
        // 如果avatar不为空，则需要头像
        if (param.getAvatar() != null && !param.getAvatar().equals(user.getAvatar())){
            user.setAvatar(param.getAvatar());
        }
        user.setUpdateUser(currUserId);
        redisUtil.deleteObject(CachePrefix.PC_USER_INFO.getCacheKey(user.getAccount()));
        return this.updateById(user);
    }

    /**
     * 修改密码
     * @param oldPassword 旧密码
     * @param newPassword 新密码
     * @param currUserId 当前用户ID
     * @return 修改结果，true为成功
     */
    @Override
    public boolean modifyUserPasswordSelf(String oldPassword, String newPassword, Long currUserId){
        User user = this.getById(currUserId);
        String old = DigestUtils.md5DigestAsHex((user.getSalt() + oldPassword).getBytes());
        if (!user.getPassword().equals(old)) {
            return false;
        }
        String salt = UUID.randomUUID().toString();
        String pwd = DigestUtils.md5DigestAsHex((salt + newPassword).getBytes());
        user.setSalt(salt);
        user.setPassword(pwd);
        user.setUpdateUser(currUserId);
        redisUtil.deleteObject(CachePrefix.PC_USER_INFO.getCacheKey(user.getAccount()));
        return this.updateById(user);
    }

    /**
     * 获取学生用户信息
     * @param currUserId 当前登录用户ID
     * @return 用户信息
     */
    @Override
    public StuUserInfoResult getStuUserInfo(Long currUserId){
        User user = this.getById(currUserId);
        StuUserInfoResult stuUserInfoResult = new StuUserInfoResult();
        BeanUtils.copyProperties(user, stuUserInfoResult);
        if (user.getUserType().equals(2)) {
            // 如果是普通学生
            Student student = studentService.getStudentByAccount(user.getAccount());
            List<Student> list = new ArrayList<>();
            list.add(student);
            List<StudentListResult> studentListResults = studentService.transferStudentToListResult(list);
            BeanUtils.copyProperties(studentListResults.get(0), stuUserInfoResult, "id");
        }else if(user.getUserType().equals(3)){
            // 如果是社会化学员
            SocialStudent socialStudent = socialStudentService.getSocialStudentByAccount(user.getAccount());
            List<SocialStudent> list = new ArrayList<>();
            list.add(socialStudent);
            List<SocialStudentListResult> socialStudentListResults = socialStudentService.transferSocialStudentToListResult(list);
            BeanUtils.copyProperties(socialStudentListResults.get(0), stuUserInfoResult, "id");
        }else {
            throw new UserTypeNotInvalidException("当前非学生账户");
        }
        // 判断是否有头像
        if (user.getAvatar() != null){
            Image image = imageService.getImageById(user.getAvatar());
            stuUserInfoResult.setAvatarUrl(image != null ? image.getRemoteFileUrl() : null);
        }
        // 判断微信是否绑定
        LambdaQueryWrapper<WxUser> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(WxUser::getUserId, user.getId());
            stuUserInfoResult.setWxBind(wxUserService.count(queryWrapper) == 0 ? 0 : 1);
        return stuUserInfoResult;
    }

    /**
     * 修改学生用户头像
     * @param file 头像文件
     * @param currUserId 当前用户ID
     * @return 响应结果
     * @throws Exception 异常
     */
    @Override
    @Transactional
    public Map<String, String> updateStuAvatar(MultipartFile file, Long currUserId) throws Exception {
        Map<String, String> map = null;
        User user = this.getById(currUserId);
        ImageUploadDto imageUploadDto = new ImageUploadDto("头像" + user.getAccount(), AppProperties.IMAGE_TYPE_AVATAR, 0, file);
        map = imageService.uploadSingle(imageUploadDto, user.getId());
        String imageId = map.get("imageId");
        user.setAvatar(Long.parseLong(imageId));
        user.setUpdateUser(currUserId);
        redisUtil.deleteObject(CachePrefix.PC_USER_INFO.getCacheKey(user.getAccount()));
        return this.updateById(user) ? map : null;
    }

    /**
     * 学生修改个人信息
     * @param param 参数
     * @param currUserId 当前登录用户ID
     * @return 处理结果，true为成功
     */
    @Override
    public boolean modifyStuUserInfoSelf(StuUserInfoUpdateParam param, Long currUserId){
        User user = this.getById(currUserId);
        if (user == null) {
            throw new EntityInvalidException("用户不存在");
        }
        user.setNickname(param.getNickname());
        user.setGender(param.getGender());
        user.setEmail(param.getEmail());
        user.setPhone(param.getPhone());
        user.setUpdateUser(currUserId);
        redisUtil.deleteObject(CachePrefix.PC_USER_INFO.getCacheKey(user.getAccount()));
        return this.updateById(user);
    }

}
